package com.freewater.springboot.demo.core.resolvabletype.eg1;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;

/**
 * ResolvableType这个类的基础用法：
 * 1、类型变量(TypeVariable)，只允许有上界（extends）；
 * 2、通配符类型变量(WildcardType)，可以有上界（extends），也可以有下届（super）；
 * 3、通配符变量(WildcardType)可以认为是一种特殊的类型变量(既可以有上界，又可以有下界)；
 * 4、注解（@Target(ElementType.TYPE_USE)），代表只要是使用了类型的地方都可以使用这种类型的注解；
 */
@Slf4j
public class Main<@MyTypeParameter T extends @MyTypeUse(whyUse = "I want use, haha") Number & @MyTypeUse Serializable> { // T是类型变量，且被注解

    private HashMap<String, List<Integer>> param; // ParameterizedType

    private T t; // TypeVariable

    private HashMap<Set<ArrayList<String>>, List<LinkedHashMap<Integer, Number>>> otherParam; // 嵌套多层的ParameterizedType

    // WildcardType 通配符类型既有上界也有下届
    public void process(List<? super Number> list, Set<? extends String> set) {
        throw new UnsupportedOperationException();
    }

    // R是泛型方法的类型变量，只能extends不能super，即只能由上边界
    public <R extends Number & Serializable> void doSomething(Set<R> set) {
        throw new UnsupportedOperationException();
    }

    public static void main(String[] args) throws NoSuchFieldException {
        // 使用JDK反射API实现
        final Field field = Main.class.getDeclaredField("param");
        ParameterizedType paramType = (ParameterizedType) field.getGenericType();
        final Type[] typeArguments = paramType.getActualTypeArguments();
        log.info("从 HashMap<String, List<Integer>> 中获取HashMap: {}", paramType.getRawType());
        log.info("从 HashMap<String, List<Integer>> 中获取String: {}", typeArguments[0]);
        log.info("从 HashMap<String, List<Integer>> 中获取List<Integer>: {}", typeArguments[1]);
        log.info("从 HashMap<String, List<Integer>> 中获取List: {}", ((ParameterizedType) typeArguments[1]).getRawType());
        log.info("从 HashMap<String, List<Integer>> 中获取Integer: {}", ((ParameterizedType) typeArguments[1]).getActualTypeArguments()[0]);
        log.info("从 HashMap<String, List<Integer>> 中获取父类: {}", field.getType().getGenericSuperclass());
        final Field tField = Main.class.getDeclaredField("t");
        final TypeVariable tType = (TypeVariable) tField.getGenericType();
        log.info("从 T 中获取Bounds: {}", Arrays.toString(tType.getBounds()));
        log.info("T 是在什么对象中声明的: {}", tType.getGenericDeclaration());
        log.info("从 Main 中得到的类型变量等于从字段t中得到的: {}", tType.getGenericDeclaration().getTypeParameters()[0].equals(tType));
        // 分割线
        log.info("-----------------------------------------------");
        // 用Spring提供的ResolvableType实现
        ResolvableType paramResolvableType = ResolvableType.forField(Main.class.getDeclaredField("param"));
        log.info("从 HashMap<String, List<Integer>> 中获取HashMap: {}", paramResolvableType.resolve());
        log.info("从 HashMap<String, List<Integer>> 中获取String: {}", paramResolvableType.getGeneric(0).resolve());
        log.info("从 HashMap<String, List<Integer>> 中获取List<Integer>: {}", paramResolvableType.getGeneric(1));
        log.info("从 HashMap<String, List<Integer>> 中获取List: {}", paramResolvableType.getGeneric(1).resolve());
        log.info("从 HashMap<String, List<Integer>> 中获取Integer: {}", paramResolvableType.getGeneric(1, 0));
        log.info("从 HashMap<String, List<Integer>> 中获取父类: {}", paramResolvableType.getSuperType());
        ResolvableType tResolvableType = ResolvableType.forField(tField);
        TypeVariable tTypeVariable = (TypeVariable) tResolvableType.getType();
        log.info("从 T 中获取Bounds: {}", Arrays.toString(tTypeVariable.getBounds()));
        log.info("T 是在什么对象中声明的: {}", tTypeVariable.getGenericDeclaration());
        log.info("从 Main 中得到的类型变量等于从字段t中得到的: {}", tTypeVariable.getGenericDeclaration().getTypeParameters()[0].equals(tType));
        // getNested方法的使用
        log.info("-----------------------------------------------");
        ResolvableType otherParam = ResolvableType.forField(Main.class.getDeclaredField("otherParam"));
        Map<Integer, Integer> typeIndexesPerLevel = new HashMap<>();
        typeIndexesPerLevel.put(2, 0);
        typeIndexesPerLevel.put(3, 0);
        log.info("从 HashMap<Set<ArrayList<String>>, List<LinkedHashMap<Integer, Number>>> 中取第一个3层(ArrayList<String>): {}",
                otherParam.getNested(3, typeIndexesPerLevel));
        // 上面的等同于
        log.info("从 HashMap<Set<ArrayList<String>>, List<LinkedHashMap<Integer, Number>>> 中取第一个3层(ArrayList<String>): {}",
                otherParam.getGeneric(0, 0));
        typeIndexesPerLevel.clear();
        typeIndexesPerLevel.put(2, 1);
        typeIndexesPerLevel.put(3, 0);
        log.info("从 HashMap<Set<ArrayList<String>>, List<LinkedHashMap<Integer, Number>>> 中取第二个3层(LinkedHashMap<Integer, Number>): {}",
                otherParam.getNested(3, typeIndexesPerLevel));
        // 上面的等同于
        log.info("从 HashMap<Set<ArrayList<String>>, List<LinkedHashMap<Integer, Number>>> 中取第二个3层(LinkedHashMap<Integer, Number>): {}",
                otherParam.getGeneric(1, 0));
        // 获取泛型上界、下届
        log.info("-----------------------------------------------");
        final Method process = Arrays.stream(Main.class.getMethods()).filter(s -> "process".equals(s.getName())).findFirst().get();
        final ResolvableType processMethodParamType1 = ResolvableType.forMethodParameter(new MethodParameter(process, 0));
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第一个参数类型: {}", processMethodParamType1);
        Type paramGenericType = processMethodParamType1.getGeneric(0).getType();
        Type[] lowerBounds = ((WildcardType) paramGenericType).getLowerBounds();
        Type[] upperBounds = ((WildcardType) paramGenericType).getUpperBounds();
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第一个参数类型中的通配符泛型下界：{}", Arrays.toString(lowerBounds));
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第一个参数类型中的通配符泛型上界：{}", Arrays.toString(upperBounds));
        // 获取泛型上界、下届
        log.info("-----------------------------------------------");
        final ResolvableType processMethodParamType2 = ResolvableType.forMethodParameter(new MethodParameter(process, 1));
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第二个参数类型: {}", processMethodParamType2);
        paramGenericType = processMethodParamType2.getGeneric(0).getType();
        lowerBounds = ((WildcardType) paramGenericType).getLowerBounds();
        upperBounds = ((WildcardType) paramGenericType).getUpperBounds();
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第二个参数类型中的通配符泛型下界：{}", Arrays.toString(lowerBounds));
        log.info("Main的方法 public void process(List<? super Number> list, Set<? extends String> set) 的第二个参数类型中的通配符泛型上界：{}", Arrays.toString(upperBounds));
        // 获取泛型方法的类型参数
        log.info("-----------------------------------------------");
        final Method doSomething = Arrays.stream(Main.class.getMethods()).filter(s -> "doSomething".equals(s.getName())).findFirst().get();
        final ResolvableType doSomethingMethodParameterType = ResolvableType.forMethodParameter(new MethodParameter(doSomething, 0));
        paramGenericType = doSomethingMethodParameterType.getGeneric(0).getType();
        upperBounds = ((TypeVariable) paramGenericType).getBounds(); // TypeVariable只有上边界
        log.info("Main的方法 public <T extends Number & Serializable> void doSomething(Set<T> set) 的参数类型中的通配符泛型上界：{}", Arrays.toString(upperBounds));
        // 获取类型参数依附的类型
        log.info("-----------------------------------------------");
        final ResolvableType classResolvableType = ResolvableType.forClass(Main.class);
        final ResolvableType generic = classResolvableType.getGenerics()[0];
        final TypeVariable classTypeVariable = (TypeVariable) generic.getType();
        log.info("Main 的类型变量T依附在Main中：{}", classTypeVariable.getGenericDeclaration());
        log.info("Main 的类型变量T拥有的注解：{}", Arrays.toString(classTypeVariable.getAnnotations()));
        final AnnotatedType[] upperBoundsUseAnnotations = classTypeVariable.getAnnotatedBounds();
        for (int i = 0; i < upperBoundsUseAnnotations.length; i++) {
            final AnnotatedType annotatedType = upperBoundsUseAnnotations[i];
            log.info("Main 的类型变量T拥有的upperBoundsTypeUseAnnotation {}: {}", i, Arrays.toString(annotatedType.getAnnotations()));
        }
    }
}
